package com.jiangyg.mall.cart.service;

import com.jiangyg.mall.cart.constant.CartConstant;
import com.jiangyg.mall.cart.dto.AddSkuDTO;
import com.jiangyg.mall.cart.dto.CheckedSkuDTO;
import com.jiangyg.mall.cart.dto.UpdateCountDTO;
import com.jiangyg.mall.cart.vo.CartItemVO;
import com.jiangyg.mall.cart.vo.CartVO;
import com.jiangyg.mall.core.constant.CoreConstant;
import com.jiangyg.mall.core.exception.BusinessException;
import com.jiangyg.mall.core.support.env.EnvConstant;
import com.jiangyg.mall.core.utils.ArithUtils;
import com.jiangyg.mall.product.service.SkuService;
import com.jiangyg.mall.product.vo.SkuBriefInfoVO;
import com.jiangyg.mall.search.service.SkuSearchService;
import com.jiangyg.mall.search.vo.SkuDetailsVO;
import com.jiangyg.mall.ware.service.WareSkuService;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.dubbo.config.annotation.Reference;
import org.apache.dubbo.config.annotation.Service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.task.TaskExecutor;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;

import java.io.IOException;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

@Service
public class CartServiceImpl implements CartService {

    private final TaskExecutor executor;

//    @Reference
    @Autowired
    private SkuService skuService;

//    @Reference
    @Autowired
    private WareSkuService wareSkuService;

//    @Reference
    @Autowired
    private SkuSearchService skuSearchService;

    private final HashOperations<String, Object, Object> hashOperations;

    @Autowired
    public CartServiceImpl(RedisTemplate<String, Object> redisTemplate,
                           @Qualifier(CoreConstant.ASYNC_EXECUTOR) TaskExecutor executor) {
        this.hashOperations = redisTemplate.opsForHash();
        this.executor = executor;
    }

    @Override
    public CartVO info(Long memberId) throws Exception {
        final CartVO cart = new CartVO();
        // 1. 查询购物车数据项列表
        cart.setItemList(selectItemList(memberId, false));
        // 2. 计算购物车总价
        final double amount = cart.getItemList()
                .stream()
                .filter(CartItemVO::isChecked)
                .map(CartItemVO::getAmount)
                .map(BigDecimal::new)
                .reduce(BigDecimal::add)
                .orElse(BigDecimal.ZERO)
                .doubleValue();
        cart.setAmount(amount);
        return cart;
    }

    @Override
    public List<CartItemVO> selectItemList(Long memberId, boolean checked) throws Exception {
        // 1. 购物车明细数据
        final List<CartItemVO> itemList = selectItemListByCache(memberId, checked);
        if (CollectionUtils.isEmpty(itemList)) {
            return itemList;
        }
        // 2. 获取到购物车商品ID集合
        final List<Long> skuIds = itemList.stream().map(CartItemVO::getId).map(Long::parseLong).collect(Collectors.toList());
        // 3. 反写购物车商品的SKU信息
        final CompletableFuture<Void> skuFuture = CompletableFuture
                .runAsync(() -> rewriteItemListOfSkuInfo(skuIds, itemList), executor);
        // 4. 反写商品库存信息
        final CompletableFuture<Void> wareFuture = CompletableFuture
                .runAsync(() -> rewriteItemListOfWareInfo(skuIds, itemList), executor);
        // 5. 阻塞等待所有的异步任务完成
        CompletableFuture.allOf(skuFuture, wareFuture).get();
        // 6. 按照新增时间排序
        itemList.sort(Comparator.comparingLong(CartItemVO::getDate));
        return itemList;
    }

    /**
     * 功能描述：反写购物车商品的库存信息
     *
     * @param skuIds   购物车商品ID集合
     * @param itemList 购物车商品明细
     */
    private void rewriteItemListOfWareInfo(List<Long> skuIds, List<CartItemVO> itemList) {
        // 1. 查询商品是否有库存
        final Map<Long, Boolean> inStockMap = wareSkuService.inStock(skuIds);
        // 2. 反写库存信息
        // TODO 实现库存是否充足
        itemList.forEach(item -> {
            final Boolean inStock = MapUtils.getBoolean(inStockMap, Long.parseLong(item.getId()), false);
            item.setInStock(inStock);
        });
    }

    /**
     * 功能描述：反写购物车商品的SKU信息
     *
     * @param skuIds   购物车商品ID集合
     * @param itemList 购物车商品明细
     */
    private void rewriteItemListOfSkuInfo(List<Long> skuIds, List<CartItemVO> itemList) {
        // 1. 查询SKU实时信息
        final List<SkuBriefInfoVO> skuList = this.skuService.selectSkuBriefInfoListByIds(skuIds);
        // 2. 根据查询到的SKU信息，反写购物车商品信息
        for (CartItemVO item : itemList) {
            final SkuBriefInfoVO sku = skuList.stream().filter(s -> s.getId().equals(item.getId())).findFirst().orElse(null);
            // 2.1 如果匹配到 sku 则反写价格、默认图片等（未发布的商品也属于失效状态）
            if (sku != null) {
                item.setPrice(sku.getPrice());
                item.setImage(sku.getDefaultImage());
                item.setInvalid(!sku.isPublished());
                item.setAmount(ArithUtils.mul(item.getPrice(), item.getCount()));
            }
            // 2.2 如果未匹配到 sku 则反写失效状态
            if (sku == null) {
                item.setInvalid(true);
            }
        }
    }

    /**
     * 功能描述：从缓存中查询购物车明细
     *
     * @param memberId 会员ID
     * @param checked  是否只查询勾选数据
     * @return 购物车明细
     */
    private List<CartItemVO> selectItemListByCache(Long memberId, boolean checked) {
        // 1. 购物车缓存键
        String cacheKey = CartConstant.cachekey(memberId);
        // 2. 获取会员购物车所有商品信息，如果购物车为空则直接返回
        final List<Object> dataList = hashOperations.values(cacheKey);
        if (CollectionUtils.isEmpty(dataList)) {
            return Collections.emptyList();
        }
        // 3. 强转类型 + 过滤
        return dataList.stream()
                .map(item -> (CartItemVO) item)
                .filter(item -> !checked || item.isChecked())
                .collect(Collectors.toList());
    }

    @Override
    public void addItem(AddSkuDTO dto, Long memberId) throws IOException {
        final String skuIdString = String.valueOf(dto.getSkuId());
        // 1. 购物车缓存键
        final String cacheKey = CartConstant.cachekey(memberId);
        // 2. 从 缓存查询 是否有该商品
        CartItemVO item = (CartItemVO) hashOperations.get(cacheKey, skuIdString);
        // 2.1 如果缓存没有该商品，则新增
        if (item == null) {
            // 2.1.1 判断购物车商品数量是否超上限
            final Long size = hashOperations.size(cacheKey);
            if (size > EnvConstant.getInt("cart.maximum")) {
                throw BusinessException.instance("mall.cart.full");
            }
            // 2.1.2 组装购物车商品信息
            final SkuDetailsVO details = skuSearchService.details(dto.getSkuId());
            item = new CartItemVO();
            item.setId(details.getId());
            item.setTitle(details.getTitle());
            item.setChecked(true);
            item.setImage(obtainSkuDefaultImage(details));
            item.setAttrs(obtainSkuSaleAttrValues(details));
            item.setPrice(details.getPrice());
            item.setCount(dto.getCount());
            item.setSpuId(details.getSpuId());
            item.setDate(System.currentTimeMillis());
        }
        // 2.2 如果缓存已有该商品，则累加数量即可
        else {
            item.setCount(item.getCount() + dto.getCount());
        }
        // 3. 把商品信息保存到缓存中
        hashOperations.put(cacheKey, skuIdString, item);
    }

    /**
     * 功能描述：获取销售属性的值
     *
     * @param details sku 详情
     * @return 销售属性的值
     */
    private List<String> obtainSkuSaleAttrValues(SkuDetailsVO details) {
        if (CollectionUtils.isEmpty(details.getSaleAttrs())) {
            return Collections.emptyList();
        }
        return details.getSaleAttrs()
                .stream()
                .map(attr ->
                        attr.getValues()
                                .stream()
                                .filter(item -> item.getSkuIds().contains(details.getId()))
                                .map(SkuDetailsVO.SaleAttrValue::getValue)
                                .findFirst()
                                .orElse(StringUtils.EMPTY))
                .filter(StringUtils::isNotBlank)
                .collect(Collectors.toList());
    }

    /**
     * 功能描述：获取 sku 的默认图片
     *
     * @param details sku详情
     * @return 默认图片地址
     */
    private String obtainSkuDefaultImage(SkuDetailsVO details) {
        if (CollectionUtils.isEmpty(details.getImages())) {
            return StringUtils.EMPTY;
        }
        return details.getImages()
                .stream()
                .filter(SkuDetailsVO.Image::isDefault)
                .map(SkuDetailsVO.Image::getUrl)
                .findFirst()
                .orElse(details.getImages().get(0).getUrl());
    }

    @Override
    public void updateItemCount(UpdateCountDTO dto, Long memberId) {
        final String skuIdString = String.valueOf(dto.getSkuId());
        // 1. 购物车缓存键
        final String cacheKey = CartConstant.cachekey(memberId);
        // 2. 从 缓存查询 查询该商品
        CartItemVO item = (CartItemVO) hashOperations.get(cacheKey, skuIdString);
        if (item == null) {
            throw BusinessException.instance("mall.cart.notexist.sku");
        }
        // 3. 修改商品数量
        item.setCount(dto.getCount());
        hashOperations.put(cacheKey, skuIdString, item);
    }

    @Override
    public void deleteItem(Long memberId, List<Long> skuIds) {
        if (CollectionUtils.isEmpty(skuIds)) {
            return;
        }
        // 1. 购物车缓存键
        final String cacheKey = CartConstant.cachekey(memberId);
        // 2. 删除商品
        final Object[] array = skuIds.stream().map(String::valueOf).toArray();
        hashOperations.delete(cacheKey, array);
    }

    @Override
    public void checkedItem(CheckedSkuDTO dto, Long memberId) {
        if (CollectionUtils.isEmpty(dto.getSkuIds())) {
            return;
        }
        // 1. 购物车缓存键
        final String cacheKey = CartConstant.cachekey(memberId);
        // 2. 根据待勾选的 skuIds 查询缓存中的商品信息
        final List<Object> oSkuIds = dto.getSkuIds().stream().map(id -> (Object) id).collect(Collectors.toList());
        final List<Object> itemList = hashOperations.multiGet(cacheKey, oSkuIds);
        // 3. 设置勾选状态然后保存缓存
        final HashMap<String, Object> map = new HashMap<>();
        for (Object item : itemList) {
            CartItemVO vo = (CartItemVO) item;
            vo.setChecked(dto.isChecked());
            map.put(vo.getId(), vo);
        }
        hashOperations.putAll(cacheKey, map);
    }

    @Override
    public long count(Long memberId) {
        final String cacheKey = CartConstant.cachekey(memberId);
        return hashOperations.size(cacheKey);
    }

}
